#ifndef TWOLEVELMAPWALKER_H
#define TWOLEVELMAPWALKER_H

/*
这个类用于遍历infomap产生的二级.map文件。
.map文件的格式都是一样的，但对于不同的应用场景，被聚类节点类型是不同的。
因为.map文件的格式一样，所以遍历.map文件的过程也是一样。
但节点类型不一样，所以遍历时要进行的处理不一样。
这个类，这个模板类，将遍历.map文件的过程抽象了出来。
它带有一个模板参数，即节点的类型。
然后还提供了一些虚函数，供派生类对遍历过程中需要进行的个性化处理进行定制。
*/

template <class NT>
class TwoLevelMapWalker {
public:
    TwoLevelMapWalker(const string& map_file_name); // 参数为.map文件的名字
    using NodeType = NT;        // 节点类型
    using ModuleType = vector<NodeType>; // 社区类型
    void go();                           // 开始遍历.map文件
private:
    string m_map_file_name;     // .map文件的名字
    virtual void on_module_count(intmax_t module_count); // 当在.map文件中读到“社区数目*Modules”那一行时被调用
    virtual void on_node_count(intmax_t node_count);     // 当在.map文件中读到“节点数目*Nodes”那一行时被调用
    // 当需要读“节点数目*Nodes”下的各行时调用
    // 派生类必须对entry进行填充。因为我们把很重要信息都编码在了节点的label（也叫节点名）中，需要解析
    virtual void read_node(istringstream& iss, NodeType& entry) = 0;
    // 当*Nodes下一个社区结束时被调用
    // 派生类可在这个函数中进行一些处理
    virtual void process_module(ModuleType& module, int& module_no) = 0;
};


template <class NodeType>
void
TwoLevelMapWalker<NodeType>::on_module_count(intmax_t module_count)
{
    cout << "module count: " << module_count << endl;
}


template <class NodeType>
void
TwoLevelMapWalker<NodeType>::on_node_count(intmax_t node_count)
{
}


template <class NodeType>
TwoLevelMapWalker<NodeType>::TwoLevelMapWalker(const string& map_file_name)
:
    m_map_file_name(map_file_name)
{
}


template <class NodeType>
void
TwoLevelMapWalker<NodeType>::go()
{
    ifstream ifs_map(m_map_file_name);

    string line;                // .map文件中的一行文本
    bool sth_in_line;           // 因为某些特殊情况下，读出的东西又会被退回，这个标志为true，表示line中还有未处理（即退回）的东西
    int module_count;           // 社区数目
    int node_count;             // 节点数目
    int module_no;              // 社区编号，即x:y中的x
    int node_no;                // 节点编号，即x:y中的y
    string tag;
    sth_in_line = false;
    while (ifs_map) {
        getline(ifs_map, line);
        istringstream iss(line);
        iss >> tag;
        if (tag == "*Modules") {
            iss >> module_count;
            on_module_count(module_count);
        }
        else if (tag == "*Nodes") {
            iss >> node_count;
            on_node_count(node_count);
            int last_module_no = 1;
            ModuleType module;
            for (int i = 0; i < node_count; ++i) {
                if (not sth_in_line)
                    getline(ifs_map, line);
                sth_in_line = false;
                istringstream iss(line);
                iss >> module_no; // 获得x:y中的x，即社区号
                iss.ignore(); // :
                iss >> node_no; // 获得x:y中的y，即社区内部的节点号
                iss.ignore(2); // space and "

                NodeType e;
                read_node(iss, e);
                module.push_back(e);

                // 如果新的module开始了，或者后边已经没有别的module了，就进行统计
                if (module_no != last_module_no || i == node_count - 1) {
                    if (module_no != last_module_no) { // 即，如果社区号发生变化，就认为是上个社区结束，新社区开始
                        //cout << "***" << endl;
                        module.pop_back();
                        i--;    // 就当这行没处理
                        sth_in_line = true;
                    }

                    process_module(module, last_module_no);

                    //cout << module_no << endl;
                    last_module_no = module_no;
                    module.clear();
                }
            }
        }
    }
}


#endif // TWOLEVELMAPWALKER_H
